import { CommonModule } from '@angular/common';
import { ChangeDetectionStrategy, Component, Input } from '@angular/core';
import { ComponentFixture, fakeAsync, TestBed, tick, waitForAsync } from '@angular/core/testing';
import { FormsModule } from '@angular/forms';
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
import { CascaderModule } from './cascader.module';
import { CascaderOption } from './interface';

const defaultOptions = [
  {
    value: 'zhejiang',
    label: 'Zhejiang',
    children: [
      {
        value: 'hangzhou',
        label: 'Hangzhou',
        children: [
          {
            value: 'xihu',
            label: 'West Lake',
            isLeaf: true,
          },
        ],
      },
      {
        value: 'ningbo',
        label: 'Ningbo',
        isLeaf: true,
      },
    ],
  },
  {
    value: 'jiangsu',
    label: 'Jiangsu',
    children: [
      {
        value: 'nanjing',
        label: 'Nanjing',
        children: [
          {
            value: 'zhonghuamen',
            label: 'Zhong Hua Men',
            isLeaf: true,
          },
        ],
      },
    ],
  },
];

@Component({
  template: `
    <lv-cascader
      style="width: 368px"
      [lvOptions]="options"
      [(ngModel)]="values"
      [lvShowFilter]="lvShowFilter"
      [lvShowClear]="lvShowClear"
    ></lv-cascader>
  `,
  changeDetection: ChangeDetectionStrategy.OnPush,
})
class TestCascaderComponent {
  @Input() lvShowFilter = true;
  @Input() lvShowClear = true;
  options: CascaderOption[];

  values: string[] | null;

  ngOnInit(): void {
    this.options = defaultOptions;
    this.values = ['zhejiang', 'hangzhou', 'xihu'];
  }
}

@Component({
  template: `
    <lv-cascader style="width: 368px" [lvOptions]="options" [(ngModel)]="values" [lvMode]="'multiple'"></lv-cascader>
  `,
  changeDetection: ChangeDetectionStrategy.OnPush,
})
class TestMultiCascaderComponent {
  options: CascaderOption[];
  values: string[][] | null = [
    ['zhejiang', 'hangzhou', 'xihu'],
    ['zhejiang', 'ningbo'],
    ['jiangsu', 'nanjing', 'zhonghuamen'],
  ];

  ngOnInit(): void {
    this.options = defaultOptions;
  }
}

let component: TestCascaderComponent | TestMultiCascaderComponent;
let fixture: ComponentFixture<TestCascaderComponent | TestMultiCascaderComponent>;
const detectChange = () => {
  fixture.detectChanges();
  tick(500);
  fixture.detectChanges();
  tick(500);
  fixture.detectChanges();
  tick(500);
};
const getCascader = () => fixture.nativeElement.querySelector('lv-cascader') as HTMLElement;
const renderComp = fakeAsync(para => {
  for (const [key, value] of Object.entries(para)) {
    component[key] = value;
  }
  detectChange();
  getCascader().dispatchEvent(new Event('click'));
  detectChange();
  expect(fixture).toMatchSnapshot();
  expect(document.querySelector('.lv-cascader-panel')).toMatchSnapshot();
  detectChange();
});

const getOption = (i: number, j: number) =>
  document.querySelector(`.lv-cascader-panel-content div:nth-child(${i}) lv-cascader-option:nth-child(${j}) .lv-cascader-option-item`);

describe('CascaderComponent', () => {
  beforeEach(waitForAsync(() => {
    TestBed.configureTestingModule({
      declarations: [TestCascaderComponent],
      imports: [CascaderModule, CommonModule, FormsModule, NoopAnimationsModule],
      teardown: { destroyAfterEach: true },
    }).compileComponents();
  }));

  describe('render', () => {
    beforeEach(fakeAsync(() => {
      fixture = TestBed.createComponent(TestCascaderComponent);
      component = fixture.componentInstance;
      detectChange();
    }));

    it('basic', () => {
      renderComp({ lvShowFilter: false });
    });

    it('search', () => {
      renderComp({});
    });
  });

  describe('function', () => {
    beforeEach(fakeAsync(() => {
      fixture = TestBed.createComponent(TestCascaderComponent);
      component = fixture.componentInstance;
      detectChange();
    }));

    const display = () => document.querySelector('.lv-trigger') as HTMLDivElement;

    it('clear', fakeAsync(() => {
      getCascader().dispatchEvent(new Event('click'));
      detectChange();
      expect(display().textContent).toBe('Zhejiang / Hangzhou / West Lake');
      const getClearButton = () => document.querySelector('lv-cascader .lv-select-clear i') as HTMLElement;
      getClearButton().dispatchEvent(new Event('click'));
      detectChange();
      expect(display().textContent).toBe('');
    }));

    it('select', fakeAsync(() => {
      getCascader().dispatchEvent(new Event('click'));
      detectChange();
      const getNingbo = () => getOption(2, 2);
      getNingbo().dispatchEvent(new Event('click'));
      detectChange();
      expect(display().textContent).toBe('Zhejiang / Ningbo');
      expect(document.querySelector('.lv-cascader-panel')).toBeNull();
      getCascader().dispatchEvent(new Event('click'));
      detectChange();
      expect(document.querySelectorAll('.lv-cascader-option-list').length).toBe(2);
      expect(
        Array.from(document.querySelectorAll('.lv-cascader-option-item-active span'))
          .map(item => item.textContent)
          .join(' / ')
      ).toBe('Zhejiang / Ningbo');
      const getJiangsu = () => getOption(1, 2);
      const getNanjing = () => getOption(2, 1);
      const getZhonghuamen = () => getOption(3, 1);
      getJiangsu().dispatchEvent(new Event('click'));
      detectChange();
      getNanjing().dispatchEvent(new Event('click'));
      detectChange();
      getZhonghuamen().dispatchEvent(new Event('click'));
      detectChange();
      expect(display().textContent).toBe('Jiangsu / Nanjing / Zhong Hua Men');
      expect(document.querySelector('.lv-cascader-panel')).toBeNull();
    }));

    it('search', fakeAsync(() => {
      getCascader().dispatchEvent(new Event('click'));
      detectChange();
      const search = (value: string) => {
        const getInput = () => document.querySelector('lv-search input') as HTMLInputElement;
        getInput().value = value;
        getInput().dispatchEvent(new Event('input'));
        detectChange();
      };
      search('zhejiang');
      expect(document.querySelector('.lv-cascader-option-item-active span').textContent).toBe('Zhejiang / Hangzhou / West Lake');
      getOption(1, 2).dispatchEvent(new Event('click'));
      detectChange();
      expect(display().textContent).toBe('Zhejiang / Ningbo');
      expect(document.querySelector('.lv-cascader-panel')).toBeNull();
      getCascader().dispatchEvent(new Event('click'));
      detectChange();
      expect(document.querySelector('.lv-cascader-option-item-active span').textContent).toBe('Zhejiang / Ningbo');
      document.querySelector('.lv-search-clear').dispatchEvent(new Event('click'));
      detectChange();
      expect(
        Array.from(document.querySelectorAll('.lv-cascader-option-item-active span'))
          .map(item => item.textContent)
          .join(' / ')
      ).toBe('Zhejiang / Ningbo');
    }));
  });
});

describe('MultiCascaderComponent', () => {
  beforeEach(waitForAsync(() => {
    TestBed.configureTestingModule({
      declarations: [TestMultiCascaderComponent],
      imports: [CascaderModule, CommonModule, FormsModule, NoopAnimationsModule],
      teardown: { destroyAfterEach: true },
    }).compileComponents();
  }));

  describe('render', () => {
    beforeEach(fakeAsync(() => {
      fixture = TestBed.createComponent(TestMultiCascaderComponent);
      component = fixture.componentInstance;
      detectChange();
    }));

    it('basic', () => {
      renderComp({ values: undefined });
    });

    it('hasValue', () => {
      renderComp({});
    });
  });

  describe('function', () => {
    beforeEach(fakeAsync(() => {
      fixture = TestBed.createComponent(TestMultiCascaderComponent);
      component = fixture.componentInstance;
      detectChange();
    }));

    const getMoreButton = () => document.querySelector('.lv-trigger-more-text');
    const getCheckedItems = (dom: HTMLElement) =>
      Array.from(dom.querySelectorAll('label.lv-checkbase-checked,label.lv-checkbox-indeterminate')).map(item => item.parentElement);
    const getOptionList = (nth: number) => document.querySelector(`.lv-cascader-option-list:nth-child(${nth})`) as HTMLElement;
    const getResultArr = () => {
      const result: string[] = [];
      const getResult = (path: string[], dom?: HTMLElement) => {
        if (!dom) {
          result.push(path.join(' / '));
          return;
        }
        getCheckedItems(dom).forEach(item => {
          path.push(item.querySelector('.lv-cascader-option-label span').textContent);
          item.dispatchEvent(new Event('click'));
          detectChange();
          getResult([...path], getOptionList(path.length + 1));
          path.pop();
        });
      };
      getResult([], getOptionList(1));
      return result;
    };

    it('removeWithPane1', fakeAsync(() => {
      getCascader().dispatchEvent(new Event('click'));
      detectChange();
      getMoreButton().dispatchEvent(new Event('click'));
      detectChange();
      expect(document.querySelectorAll('.lv-trigger-item-calc-container .lv-trigger-item').length).toBe(3);
      expect(document.querySelector('.lv-cascader-panel')).toBeTruthy();
      document.querySelector('.lv-trigger-item:nth-child(3) i').dispatchEvent(new Event('click'));
      detectChange();
      expect(document.querySelectorAll('.lv-trigger-item-calc-container .lv-trigger-item').length).toBe(2);
      expect(getResultArr().includes('Jiangsu / Nanjing / Zhong Hua Men')).toBeFalsy();
    }));

    it('removeWithPane2', fakeAsync(() => {
      getCascader().dispatchEvent(new Event('click'));
      detectChange();
      expect(getOptionList(3)).toBeTruthy();
      getOption(3, 1).dispatchEvent(new Event('click'));
      detectChange();
      expect(document.querySelectorAll('.lv-trigger-item-calc-container .lv-trigger-item').length).toBe(2);
      expect(getResultArr().includes('Jiangsu / Nanjing / Zhong Hua Men')).toBeFalsy();
      expect(
        Array.from(getOption(1, 1).querySelector('label.lv-cascader-option-checkbox').classList).includes('lv-checkbase-checked')
      ).toBeFalsy();
    }));

    it('removeWithoutPane', fakeAsync(() => {
      getCascader().dispatchEvent(new Event('click'));
      detectChange();
      getCascader().dispatchEvent(new Event('click'));
      detectChange();
      getMoreButton().dispatchEvent(new Event('click'));
      detectChange();
      expect(document.querySelectorAll('.lv-trigger-item-calc-container .lv-trigger-item').length).toBe(3);
      document.querySelector('.lv-trigger-item:nth-child(3) i').dispatchEvent(new Event('click'));
      detectChange();
      expect(document.querySelectorAll('.lv-trigger-item-calc-container .lv-trigger-item').length).toBe(2);
      expect(getResultArr().includes('Jiangsu / Nanjing / Zhong Hua Men')).toBeFalsy();
    }));

    it('select', fakeAsync(() => {
      getCascader().dispatchEvent(new Event('click'));
      detectChange();
      getOption(1, 1).querySelector('label.lv-cascader-option-checkbox').dispatchEvent(new Event('click'));
      detectChange();
      getOption(1, 2).querySelector('label.lv-cascader-option-checkbox').dispatchEvent(new Event('click'));
      detectChange();
      expect(document.querySelectorAll('.lv-trigger-item-calc-container .lv-trigger-item').length).toBe(0);
      expect(
        Array.from(getOption(1, 1).querySelector('label.lv-cascader-option-checkbox').classList).includes('lv-checkbase-no-label')
      ).toBeTruthy();
      expect(
        Array.from(getOption(1, 2).querySelector('label.lv-cascader-option-checkbox').classList).includes('lv-checkbase-no-label')
      ).toBeTruthy();
      getOption(1, 1).dispatchEvent(new Event('click'));
      detectChange();
      expect(getOptionList(2)).toBeTruthy();
      getOption(2, 1).querySelector('label.lv-cascader-option-checkbox').dispatchEvent(new Event('click'));
      detectChange();
      expect(
        Array.from(getOption(1, 1).querySelector('label.lv-cascader-option-checkbox').classList).includes('lv-checkbox-indeterminate')
      ).toBeTruthy();
      expect(document.querySelectorAll('.lv-trigger-item-calc-container .lv-trigger-item').length).toBe(1);
      expect(getResultArr().includes('Zhejiang / Hangzhou / West Lake')).toBeTruthy();
    }));
  });
});
